﻿
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows.Media;
using MyFloridaHouse.libSvOpenDistricts.Criteria;
using MyFloridaHouse.ServiceConn.DistrBldrSvcRef;
using MyFloridaHouse.libSvUtilities.CustomColors;
using MyFloridaHouse.libSvUIControls;
using MyFloridaHouse.libSvCommonUtilities;


namespace MyFloridaHouse.libSvOpenDistricts.MapShapes
{
    public class QueryCriteriaData
    {
        public int layerID;
        public int regionID;
        public int dictionaryID;
        public string dataValue;
        public int data = 0;

        public QueryCriteriaData(int layerID, int regionID, int dictionaryID, string dataValue)
        {
            this.layerID = layerID;
            this.regionID = regionID;
            this.dictionaryID = dictionaryID;
            this.dataValue = dataValue;
        }

    }
    public partial class  RegionFill : INotifyPropertyChanged
    {
        #region propertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion

        #region public properties

        public const string CriteriaSubscriberName = "RegionFill";

        private bool _isRegionFillOn = true;
        public bool isRegionFillOn
        {
            get { return _isRegionFillOn; }
            set
            {
                if (_isRegionFillOn != value)
                {
                    _isRegionFillOn = value;
                    NotifyPropertyChanged("isRegionFillOn");
                    RaiseRegionFillConditionsChanged(this);
                }
            }
        }

        //public int dataItemDictionaryID
        //{
        //    get
        //    {
        //        if ((selectedDatasetCriteriaIndex >= 0) & (selectedDatasetCriteriaIndex <= datasetCriteriaList.Count))
        //        {
        //            return datasetCriteriaList[selectedDatasetCriteriaIndex].dictionaryList[0].dictionaryID;
        //        }
        //        else
        //        {
        //            return -1;
        //        }
        //    }
        //    set { }
        //}

        //public string dataItemHeader
        //{
        //    get
        //    {
        //        if ((selectedDatasetCriteriaIndex >= 0) & (selectedDatasetCriteriaIndex <= datasetCriteriaList.Count))
        //        {
        //            return datasetCriteriaList[selectedDatasetCriteriaIndex].criteriaHeader;
        //        }
        //        else
        //        {
        //            return "";
        //        }
        //    }
        //    set { }
        //}

        private int _opacity = 50;
        public int opacity
        {
            get { return _opacity; }
            set
            {
                if (_opacity != value)
                {
                    _opacity = value; NotifyPropertyChanged("opacity");
                    RaiseRegionFillConditionsChanged(this);
                }
            }
        }

        private SolidColorBrush _shadingMinBrush = new SolidColorBrush(Colors.White);
        public SolidColorBrush shadingMinBrush
        {
            get { return _shadingMinBrush; }
        }

        private SolidColorBrush _shadingMaxBrush = new SolidColorBrush(Colors.Red);
        public SolidColorBrush shadingMaxBrush
        {
            get { return _shadingMaxBrush; }
        }

        private string _criteriaDatasetName = "";
        public string criteriaDatasetName
        {
            get { return _criteriaDatasetName; }
            set
            {
                if (_criteriaDatasetName != value)
                {
                    _criteriaDatasetName = value; NotifyPropertyChanged("criteriaDatasetName");
                    RaiseRegionFillCriteriaChanged(this);
                }
            }
        }

        private ObservableCollection<CriteriaDatasetDTO> _datasetCriteriaList;
        public ObservableCollection<CriteriaDatasetDTO> datasetCriteriaList
        {
            get { return _datasetCriteriaList; }
            set
            {
                if (_datasetCriteriaList != value)
                {
                    _datasetCriteriaList = value; NotifyPropertyChanged("datasetCriteriaList");
                    // don't need to RaiseRegionFillChanged because it is not a primary driver
                }
            }
        }

        private int _selectedDatasetCriteriaIndex = -1;
        public int selectedDatasetCriteriaIndex
        {
            get { return  _selectedDatasetCriteriaIndex; }
            set
            {
                // turn off old selection
                if ((selectedDatasetCriteriaIndex >= 0) & (selectedDatasetCriteriaIndex <= datasetCriteriaList.Count))
                {
                    datasetCriteriaList[selectedDatasetCriteriaIndex].isSelected = false;
                }

                _selectedDatasetCriteriaIndex = value;

                // turn on new selection
                if ((selectedDatasetCriteriaIndex >= 0) & (selectedDatasetCriteriaIndex <= datasetCriteriaList.Count))
                {
                    datasetCriteriaList[selectedDatasetCriteriaIndex].isSelected = true;
                }
                CriteriaDatasetManager.UpdateSubscriberDisplayCriteriaIndex(CriteriaSubscriberName, selectedDatasetCriteriaIndex);

                NotifyPropertyChanged("selectedDatasetCriteriaIndex");
                RaiseRegionFillCriteriaChanged(this);
            }
        }

        private static RegionFill _regionFillInstance;
        public static RegionFill regionFillInstance
        {
            get { return _regionFillInstance; }
        }

        //private static SolidColorBrush _colorScaleMinBrush;
        public static SolidColorBrush colorScaleMinBrush
        {
            get { return new SolidColorBrush(Colors.Blue); }
        }

        public static SolidColorBrush colorScaleMaxBrush
        {
            get { return new SolidColorBrush(Colors.Red); }
        }


        private ObservableCollection<ColorScaleSection> _colorScaleSectionList;
        public ObservableCollection<ColorScaleSection> colorScaleSectionList
        {
            get { return _colorScaleSectionList; }
            set
            {
                if (_colorScaleSectionList != value)
                {
                    _colorScaleSectionList = value;
                    NotifyPropertyChanged("colorScaleSectionList");
                    RaiseRegionFillConditionsChanged(this);
                }
            }
        }

        #endregion

        #region public methods

		/// <summary>
		/// Initializes a new instance of the <see cref="RegionFill"/> class.
		/// Constructor: When the regionFill object is created, store a reference to it in the static class.
		/// </summary>
        public RegionFill()
        {
            // when the regionFill object is created, store a reference to it in the static class
            // That way anything that needs to access the instance methods can do so without passing around references all over the place
            _regionFillInstance = this;
            //RegionFill.InitializeEventListeners();

            LoadColorScaleSelectionList();

            LoadCriteriaDataset(Constants.DefaultCriteriaDatasetName);
        }

        public SolidColorBrush GetRegionFillColorBrush(List<RegionDataDTO> regionData )
        {
            SolidColorBrush regionFillColor = new SolidColorBrush(Colors.Transparent);
            if (isRegionFillOn)
            {
                if (CriteriaDataset.MeetsQueryCriteria(regionData, datasetCriteriaList))
                {
                    if ((selectedDatasetCriteriaIndex >= 0) & (selectedDatasetCriteriaIndex <= datasetCriteriaList.Count())) 
                    {
                        CriteriaDatasetDTO criteria = datasetCriteriaList[selectedDatasetCriteriaIndex];
                        int regionComputedDataValue = 0;

                        // Don't check the include/don't include boolean return, because all we want is the
                        // calculated value, we've already determined we want it based on other selected criteria.
                        byte alpha = (byte)((double)opacity / 100.0 * (double)255);
                        if (CriteriaDataset.EvaluateCriteria(regionData, criteria, false, out regionComputedDataValue))
                        {
                            // convert data value to a fill color
                            int valMin = criteria.valueMin;
                            int valMax = criteria.valueMax;

                            int min = (int)valMin;
                            int max = (int)valMax;

                            double pct = (((float)regionComputedDataValue - (float)min) / ((float)max - (float)min)) * 100; //convert to percent                    

                            regionFillColor = new SolidColorBrush(RGB.GetColorFromPercent(alpha, pct));
                        }
                        else
                        {
                            regionFillColor = new SolidColorBrush(Color.FromArgb(alpha, 211, 211, 211));
                        }
                    }
                }
            }
            return regionFillColor;
        }


        public void LoadCriteriaDataset(string newCriteriaDatasetName)
        {
            if (this.criteriaDatasetName != newCriteriaDatasetName)
            {
                this.criteriaDatasetName = newCriteriaDatasetName;
                this.datasetCriteriaList = CriteriaDatasetManager.Subscribe(newCriteriaDatasetName, RegionFill.CriteriaSubscriberName);


                foreach (var datasetMember in datasetCriteriaList)
                {
                    datasetMember.PropertyChanged += new PropertyChangedEventHandler(datasetMember_PropertyChanged);
                }
            }

        }

        public void LoadColorScaleSelectionList()
        {
            colorScaleSectionList = new ObservableCollection<ColorScaleSection>();
            for (int pct = 0; pct < 100; pct++)
            {
                colorScaleSectionList.Add(new ColorScaleSection(new SolidColorBrush(RGB.GetColorFromPercent(255, pct)), 2, pct));
            }
        }

        private void datasetMember_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.ToString() == "isQueryCriteria")
            {
                // isQueryCriteri indicates if we want that criteria condition included in the decision to show or not.
                RaiseRegionFillCriteriaChanged(this);
            }
            else
            {
                RaiseRegionFillConditionsChanged(this);
            }
        }



        public void AggregateLayerData(int layerID, ObservableCollection<RegionDataDTO> regionsDataList)
        {
            ZeroCritieriaItemsMinMax(layerID);

            layerID = AdjustLayerIDforConfigLayers(layerID);

            // regionsDataList holds all data for all regions currently in view
            // calculate min and max for each type of data loaded across all the regions in the list
            var distinctDictionaryIDQuery = (from regionData in regionsDataList
                                             select regionData.dictionaryID).Distinct();
            foreach (var dictionaryID in distinctDictionaryIDQuery)
            {
                try
                {
                    int max = (from dat in regionsDataList
                               where ((dat.dictionaryID == dictionaryID) & (dat.sourceLayerID == layerID))
                               select Int32.Parse(dat.dataValue)).Max();

                    int min = (from dat in regionsDataList
                               where ((dat.dictionaryID == dictionaryID) & (dat.sourceLayerID == layerID))
                               select Int32.Parse(dat.dataValue)).Min();

                    LoadLayerMinMax(layerID, dictionaryID, min, max);
                }
                catch (Exception e)
                {
                    //TODO: Log this error converting data to an int for aggregation
                    // don't want to report it to user, could be hundreds of error if we try to convert a string column
                }
            }
        }

        private int AdjustLayerIDforConfigLayers(int layerID)
        {
            // special case, a single data item (like totalPopulation) has the same dictionaryID across the config layers
            // todo: change the hardcode 1, or 2, and get config info from layer table, will need to pass configLayerList in on RegionFill Constructor, current config layers are 0,1,2
            if ((layerID == 1) | (layerID == 2))
            {
                return 0;
            }
            else
            {
                return layerID;
            }
        }


        private void ZeroCritieriaItemsMinMax(int layerID)
        {
            // datasetMemberList holds all criteria in the currently selected criteria dataset
            foreach (var criteria in datasetCriteriaList)
            {
                foreach (var dictionaryItem in criteria.dictionaryList)
                {
                    dictionaryItem.min = 0;
                    dictionaryItem.max = 0;
                }
            }
        }

        private void LoadLayerMinMax(int layerID, int dictionaryID, int min, int max)
        {
            foreach (var criteria in datasetCriteriaList)
            {
                var dictionaryListQuery = from dictionaryItem in criteria.dictionaryList
                                          where dictionaryItem.dictionaryID == dictionaryID
                                          & dictionaryItem.layerID == layerID
                                          select dictionaryItem;

                foreach (var dictionaryItem in dictionaryListQuery)
                {
                    dictionaryItem.min = min;
                    dictionaryItem.max = max;
                }

                // now every value in the criteria is loaded, can tell what the actual value max/mins are
                //todo: Need to verify these max/mins are calulated correctly
                if (criteria.criteriaType == enumCriteriaType.Total)
                {
                    criteria.valueMax = criteria.dictionaryList[0].max;
                    criteria.valueMin = criteria.dictionaryList[0].min;
                }
                else if (criteria.criteriaType == enumCriteriaType.TotalDifference)
                {
                    criteria.valueMax = criteria.dictionaryList[0].max - criteria.dictionaryList[1].min;
                    criteria.valueMin = criteria.dictionaryList[0].min - criteria.dictionaryList[1].max;
                }
                //TODO: Calculate the percentages for criteria for regions in view - not sure we want to do this, would have to calculate values for every item on screen.
                // NOTE: we default to 0/100.  Also, may only want to calculate for items where the criteria selection is picked.
                // example, for percent 1) if criteria.isSelected then 2) loop through all regions, calulate the values, and take the min/max over all the regions  ==> may not be worth it
                // it's even worse for percent differences
                //else if (criteria.criteriaType == enumCriteriaType.Percent)
                //{
                //    criteria.valueMax = criteria.dictionaryList[0].max / criteria.dictionaryList[1].min;
                //    criteria.valueMin = criteria.dictionaryList[0].min / criteria.dictionaryList[1].max;
                //}
                //else if (criteria.criteriaType == enumCriteriaType.PercentDifference)
                //{
                //    criteria.valueMax = (criteria.dictionaryList[0].max / criteria.dictionaryList[1].min) - (criteria.dictionaryList[2].max / criteria.dictionaryList[3].min);
                //    criteria.valueMin = (criteria.dictionaryList[0].min / criteria.dictionaryList[1].max) - (criteria.dictionaryList[2].min / criteria.dictionaryList[3].max);
                //}
            }
        }
        #endregion

    }
}
